package com.hunliji.monitor.share.component.lock.impl;

import com.hunliji.monitor.share.component.lock.DistributedLockFacade;
import com.hunliji.monitor.share.component.lock.config.DistributedLockConfig;
import com.hunliji.monitor.share.component.lock.enums.DistributedLockConstants;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁
 * todo 目前存在的问题
 * 在切换系统的实现方式的时候，会出现一致性问题，但是现在只有一种实现，不会出现
 * @author wuchen
 */
@Component
public class DistributedLockFacadeImpl implements DistributedLockFacade {
    private static final Logger logger = LoggerFactory.getLogger(DistributedLockFacadeImpl.class);
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private DistributedLockConfig distributedLockConfig;
    /**
     * 基于Redisson加锁的方式
     * @param key：key
     * @param timeout：获取锁的超时时间
     * @param expires：持有锁的有效时间
     * @param timeType：时间单位类型{@link DistributedLockConstants.TimeType}
     */
    private Boolean lockByRedisson(String key, Long timeout, Long expires,DistributedLockConstants.TimeType timeType){
        RLock lock = redissonClient.getLock(key);
        TimeUnit timeUnit = TimeUnit.MILLISECONDS;
        if(DistributedLockConstants.TimeType.MILLISECONDS.name().equals(timeType.name())){
            timeUnit = TimeUnit.MILLISECONDS;
        }
        if(DistributedLockConstants.TimeType.SECONDS.name().equals(timeType.name())){
            timeUnit = TimeUnit.SECONDS;
        }
        try {
            if(lock.tryLock(timeout,expires, timeUnit)){
                return true;
            }else {
                return false;
            }
        } catch (Exception e) {
            logger.error("business 分布式锁获取发生异常",e);
            return false;
        }
    }

    /**
     * 基于Redisson解锁方式
     * @param key：key
     */
    private void unLockByRedisson(String key){
        RLock lock = redissonClient.getLock(key);
        lock.unlock();
    }
    /**
     * 分布式锁上锁
     * @param key：key
     * @param timeout：获取锁的超时时间
     * @param expires：持有锁的有效时间
     * @param timeType：时间单位类型{@link DistributedLockConstants.TimeType}
     */
    @Override
    public Boolean lock(String key, Long timeout, Long expires,DistributedLockConstants.TimeType timeType) {
        String techType = distributedLockConfig.getTechType();
        if(StringUtils.isBlank(techType)){
            techType = DistributedLockConstants.DEFAULT_TECH_TYPE;
        }
        for (int i = 0 ; i<=DistributedLockConstants.DEFAULT_RETRY_COUNT;i++){
            //如果采用redisson
            if(DistributedLockConstants.TechType.REDISSON.name().equals(techType)){
                Boolean lockSuccess = lockByRedisson(key,timeout,expires,timeType);
                if(lockSuccess){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 分布式锁的上锁
     * 默认使用毫秒作为时间计数单位
     * @param key：key
     * @param timeout：获取锁的超时时间
     * @param expires：持有锁的有效时间
     * @return
     */
    @Override
    public Boolean lock(String key, Long timeout, Long expires) {
        return lock(key,timeout,expires,DistributedLockConstants.TimeType.MILLISECONDS);
    }

    /**
     * 分布式锁的上锁
     * 默认使用毫秒作为时间计数单位
     * 默认使用超时时间 = 50ms
     * 默认使用过期释放时间 = 1000ms
     * @param key：key
     * @return
     */
    @Override
    public Boolean lock(String key) {
        return lock(key,DistributedLockConstants.DEFAULT_TIME_OUT,DistributedLockConstants.DEFAULT_EXPIRES, DistributedLockConstants.TimeType.MILLISECONDS);
    }

    /**
     * 解锁
     * @param key：key
     */
    @Override
    public void unLock(String key) {
        String techType = distributedLockConfig.getTechType();
        if(StringUtils.isBlank(techType)){
            techType = DistributedLockConstants.DEFAULT_TECH_TYPE;
        }
        if(DistributedLockConstants.TechType.REDISSON.name().equals(techType)){
            unLockByRedisson(key);
        }

    }




}
